/*
 * Copyright (c) 2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "./GLPrivate.h"

#include "alkes/opengl/GLVertexBufferVBO.h"
#include "alkes/opengl/GLGraphics.h"
#include "alkes/opengl/GLState.h"
#include "alkes/opengl/GLProgram.h"

namespace alkes {

#if !defined(ALKES_OGL_DISABLE_VBO)

GLVertexBufferVBO* GLVertexBufferVBO::create(uint32_t vertex_type)
{
    if (0 == vertex_type)
        return false;

    return new GLVertexBufferVBO(vertex_type);
}

void GLVertexBufferVBO::destroy()
{
    delete this;
}

GLVertexBufferVBO::GLVertexBufferVBO(uint32_t vertex_type)
: GLVertexBuffer(vertex_type)
{
}

GLVertexBufferVBO::~GLVertexBufferVBO()
{
}

bool GLVertexBufferVBO::onSetupVertex(GLGraphics* graphics, GLState& state, uint32_t& start, uint32_t& count)
{
    bool ret = false;

    if (graphics->supportsVBO())
    {
        bool modified;

        // Enable VBO
        graphics->bindBuffer(GL_ARRAY_BUFFER, getGLBuffer(), modified);
        if (!modified && !isDirty())
        {
            if ((graphics->isActiveShader() && flag(AL_VBOFLAG_USED_SHADER))
                || !graphics->isActiveShader() && !flag(AL_VBOFLAG_USED_SHADER))
            {
                return true;    // Do nothing.
            }
        }

        if (isDirty())
        {
            graphics->bufferData(GL_ARRAY_BUFFER, getVertexSize() * getVertexCount(), getBuffer(0, getVertexCount()), GL_STATIC_DRAW);
            setDirty(false);
        }

        if (graphics->isActiveShader())
        {
            int32_t num_of_traits;
            const VertexTypeTrait* traits = getVertexTypeTraits(num_of_traits);
            if (!traits)
                return false;

            uint32_t vtype = getVertexType();
            uint32_t stride = getVertexSize();
            uint8_t* p = (uint8_t*)0;

            int32_t i, idx;
            GLint loc;

            for (i = 0, idx = 0; i < num_of_traits; ++i, ++traits)
            {
                if (0 == (traits->type & vtype))
                {
                    loc = graphics->getAttribLocation(getAttribName((VertexType)traits->type));
                    if (loc != -1)
                    {
                        graphics->disableVertexAttribArray(loc);
                    }
                    continue;
                }

                switch (traits->type & vtype)
                {
                case AL_VERTEX_XY:
                case AL_VERTEX_XYZ:
                    loc = graphics->getAttribLocation(getAttribName(AL_VERTEX_XYZ));
                    if (loc != -1)
                    {
                        graphics->enableVertexAttribArray(loc);
                        graphics->vertexAttribPointer(loc, 3, GL_FLOAT, GL_FALSE, stride, p + getOffsetOf(AL_VERTEX_XYZ));
                    }
                    break;
                case AL_VERTEX_COLOR:
                    loc = graphics->getAttribLocation(getAttribName(AL_VERTEX_COLOR));
                    if (loc != -1)
                    {
                        graphics->enableVertexAttribArray(loc);
                        graphics->vertexAttribPointer(loc, 4, GL_UNSIGNED_BYTE, GL_FALSE, stride, p + getOffsetOf(AL_VERTEX_COLOR));
                    }
                    break;
                case AL_VERTEX_UV1:
                    loc = graphics->getAttribLocation(getAttribName(AL_VERTEX_UV1));
                    if (loc != -1)
                    {
                        graphics->enableVertexAttribArray(loc);
                        graphics->vertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, stride, p + getOffsetOf(AL_VERTEX_UV1));
                    }
                    break;
                default:
                    break;
                }
            }

            setFlag(AL_VBOFLAG_USED_SHADER, true);
            ret = true;
        }
        else
        {
            ret = setupVertexArray(graphics, state, NULL);
            setFlag(AL_VBOFLAG_USED_SHADER, false);
        }
    }
    else
    {
        ret = GLVertexBuffer::onSetupVertex(graphics, state, start, count);
    }

    return ret;
}

#endif

}
